/*
Copyright vramesh2,crashtc2,bglin2
(Separate netids with a comma)

*/
/* MP2:Simple Shell */

/* LIBRARY SECTION */
#include <ctype.h>              /* Character types                       */
#include <stdio.h>              /* Standard buffered input/output        */
#include <stdlib.h>             /* Standard library functions            */
#include <string.h>             /* String operations                     */
#include <sys/types.h>          /* Data types                            */
#include <sys/wait.h>           /* Declarations for waiting              */
#include <unistd.h>             /* Standard symbolic constants and types */

#include "mp2_tests.h"         /* Built-in test system                  */

/* DEFINE SECTION */
#define SHELL_BUFFER_SIZE 256   /* Size of the Shell input buffer        */
#define SHELL_MAX_ARGS 8        /* Maximum number of arguments parsed    */

/* VARIABLE SECTION */
enum { STATE_SPACE, STATE_NON_SPACE };   /* Parser states */
int command = 1, filled = 0, shell_level = 1;
typedef struct history_arg
{
   char argv[SHELL_MAX_ARGS + 1][257];
   int argc;
} history_arg ;
history_arg history[10];

int imthechild(const char *path_to_exec, char *const args[])
{
   char *path, *token;
   path = getenv ( "PATH" );
   char path_to_try [1024];
   if ( access ( path_to_exec, X_OK ) != -1 )
   {
      return execv (path_to_exec, args) ? -1 : 0;
   }
   else
   {
      for (( token = strtok ( path, ":" )); token; ( token = strtok ( NULL, ":" )))
      {
         strcpy ( path_to_try, token );
         strcat ( path_to_try, "/");
         strcat ( path_to_try, path_to_exec );
//         printf ( "%s%s\n", path_to_try,args );
         if ( access ( path_to_try, X_OK ) != -1 )
         {
            return execv ( path_to_try, args )  ? -1 : 0;
         }
      }
      return -1;
   }
}

void imtheparent(pid_t child_pid, int run_in_background)
{
   int child_return_val, child_error_code;

   /* fork returned a positive pid so we are the parent */
   fprintf(stderr,
           "  Parent says 'child process has been forked with pid=%d'\n",
           child_pid);
   if (run_in_background) {
      fprintf(stderr,
              "  Parent says 'run_in_background=1 ... so we're not waiting for the child'\n");
      return;
   }
   waitpid ( child_pid, &child_return_val, 0);
   /* Use the WEXITSTATUS to extract the status code from the return value */
   child_error_code = WEXITSTATUS(child_return_val);
   fprintf(stderr,
           "  Parent says 'wait() returned so the child with pid=%d is finished.'\n",
           child_pid);
   if (child_error_code != 0) {
      /* Error: Child process failed. Most likely a failed exec */
      fprintf(stderr,
              "  Parent says 'Child process %d failed with code %d'\n",
              child_pid, child_error_code);
   }
}

/* Unbuffered variant of fgets */
char *ubfgets(char *buf, int len, FILE *f)
{
   int i = 0, fd = fileno(f), readcount;
   do {
      readcount = read(fd, buf+i, 1);
      i++;
   } while ((i < len-1) && (buf[i-1] != '\n') && readcount);
   buf[i] = 0;
   if ((i == 1) && (readcount == 0))
      return NULL;
   else
      return buf;
} 

/* MAIN PROCEDURE SECTION */
int mp2_main(int argc, char **argv)
{
   pid_t shell_pid, pid_from_fork;
   int n_read, i, exec_argc, parser_state, run_in_background;
   /* buffer: The Shell's input buffer. */
   char buffer[SHELL_BUFFER_SIZE];
   /* exec_argv: Arguments passed to exec call including NULL terminator. */
   char *exec_argv[SHELL_MAX_ARGS + 1];

   /* Allow the Shell prompt to display the pid of this process */
   shell_pid = getpid();

   while (1) {
   /* The Shell runs in an infinite loop, processing input. */

      fprintf(stdout, "Shell(pid=%d)%d} ", shell_pid, command);
      fflush(stdout);

      /* Read a line of input. */
      if (ubfgets(buffer, SHELL_BUFFER_SIZE, stdin) == NULL)
         return EXIT_SUCCESS;
      n_read = strlen(buffer);
      run_in_background = n_read > 2 && buffer[n_read - 2] == '&';
      buffer[n_read - run_in_background - 1] = '\n';

      /* Parse the arguments: the first argument is the file or command *
       * we want to run.                                                */

      parser_state = STATE_SPACE;
      for (exec_argc = 0, i = 0;
           (buffer[i] != '\n') && (exec_argc < SHELL_MAX_ARGS); i++) {

         if (!isspace(buffer[i])) {
            if (parser_state == STATE_SPACE)
               exec_argv[exec_argc++] = &buffer[i];
            parser_state = STATE_NON_SPACE;
         } else {
            buffer[i] = '\0';
            parser_state = STATE_SPACE;
         }
      }

      /* run_in_background is 1 if the input line's last character *
       * is an ampersand (indicating background execution).        */

   int stuff, inside;
//   for ( stuff = 0; stuff < filled; stuff++ )
//   {
//      printf ( "%d : ", stuff );
//      for ( inside = 0; inside < history [ stuff ].argc; inside++ )
//      {
//         printf ( "%s", history [ stuff ].argv [ inside ] );
//      }
//      printf ( "\n", history [ stuff ].argv [ inside ] );
//   }

      buffer[i] = '\0';   /* Terminate input, overwriting the '&' if it exists */

      /* If no command was given (empty line) the Shell just prints the prompt again */
      if (!exec_argc)
         continue;
      /* Terminate the list of exec parameters with NULL */
      exec_argv[exec_argc] = NULL;
      if (( exec_argv [ 0 ][ 0 ] == '!' ) && (( exec_argv [ 0 ][ 1 ] == '!' ) ||
         ( exec_argv [ 0 ][ 1 ] > '0' && exec_argv [ 0 ][ 0 ] <= '9')))
      {
         int index = atoi ( &exec_argv [ 0 ] [ 1 ] ) - 1;
         if ( exec_argv [ 0 ] [ 1 ] == '!' )
         {
	    /*
	    */

            if ( command == 1 )
            {
               fprintf ( stderr, "Not Valid\n" );
               fflush ( stderr );
               command --;
	         }
            else
            {
               for ( i = 0; i < history [ 9 ].argc; ++i )
               {
                  printf ( "%s ", history [ 9 ].argv [ i ]);
               }
               printf("\n");
               fflush ( stdout );
            }
            for ( i = 0; i < history [ 9 ].argc; ++i )
            {
               char* temp = malloc ( 257 );

               strcpy ( temp, history [ 9 ].argv [ i ] );
               exec_argv[i] = temp;
            }
            exec_argc = history [ 9 ].argc;
            for ( i = 0; i < history [ index ].argc; i++ )
            {
               strcpy ( history [ 9 ].argv [ i ], exec_argv [ i ] );
            }
            history [ 9 ].argc = exec_argc;
         }
         else
         {
            for ( i = 0; i < history [ index ].argc; i++ )
            {
               strcpy ( history [ 9 ].argv [ i ], history [ index ].argv [ i ] );
            }
            history [ 9 ].argc = history [ index ].argc;

            if ( filled < 9 )
            {
               for ( i = 0; i < history [ index ].argc; i++ )
               {
                  strcpy ( history [ filled ].argv [ i ], history [ index ].argv [ i ] );
               }
               history [ filled ].argc = history [ index ].argc;
            }
            for ( i = 0; i < history [ index ].argc; i++ )
            {
               char* temp = malloc ( 257 );

               strcpy ( temp, history [ index ].argv [ i ] );
               exec_argv[i] = temp;
            }
            if ( filled < index + 1 )
            {
               fprintf ( stderr, "Not valid\n" );
               fflush ( stderr );
               command--;
            }
            else
            {
               int j;
               for ( j = 0; j < history [ index ].argc; j++ )
               {
                  printf ( "%s", history [ index ].argv [ j ]);
               }
               printf ( "\n", history [ index ].argv [ j ]);
               fflush ( stdout );
            }
         }
      }
      if ( filled < 9 )
      {
         int i;
         history [ filled ].argc = 0;
         for ( i = 0; i < exec_argc; i++ )
         {
            strcpy ( history [ filled ].argv [ i ], exec_argv [ i ] );
            history [ filled ].argc++;
         };
         filled ++;
      }
         int i;
         history [ 9 ].argc = 0;
         for ( i = 0; i < exec_argc; i++ )
         {
            strcpy ( history [ 9 ].argv [ i ], exec_argv [ i ] );
            history [ 9 ].argc++;
         };

      /* If Shell runs 'exit' it exits the program. */
      if (!strcmp(exec_argv[0], "exit")) {
         printf("Exiting process %d\n", shell_pid);
         command++;
         return EXIT_SUCCESS;   /* End Shell program */
      } else if (!strcmp(exec_argv[0], "cd") && exec_argc > 1) {
      /* Running 'cd' changes the Shell's working directory. */
         /* Alternative: try chdir inside a forked child: if(fork() == 0) { */
         if (chdir(exec_argv[1]))
            /* Error: change directory failed */
            fprintf(stderr, "cd: failed to chdir %s\n", exec_argv[1]);   
         /* End alternative: exit(EXIT_SUCCESS);} */
         command++;
      } else if (!strcmp(exec_argv[0], "sub") && exec_argc == 1) {
         pid_from_fork = fork();

         if (pid_from_fork < 0) {
            /* Error: fork() failed.  Unlikely, but possible (e.g. OS *
             * kernel runs out of memory or process descriptors).     */
            fprintf(stderr, "fork failed\n");
            continue;
         }
         if (pid_from_fork == 0) {
            if (shell_level < 3) {
               command = 1;   
               filled = 0;
               shell_pid = getpid();
               ++shell_level;
            } else {
               fprintf(stderr, "Too deep!\n");
               fflush(stderr);
            }
         } else {
            imtheparent(pid_from_fork, run_in_background);
            /* Parent will continue around the loop. */
         }
      } else {
      /* Execute Commands */
         /* Try replacing 'fork()' with '0'.  What happens? */
         pid_from_fork = fork();

         if (pid_from_fork < 0) {
            /* Error: fork() failed.  Unlikely, but possible (e.g. OS *
             * kernel runs out of memory or process descriptors).     */
            fprintf(stderr, "fork failed\n");
            continue;
         }
         if (pid_from_fork == 0) {
            return imthechild(exec_argv[0], &exec_argv[0]);
            /* Exit from main. */
         } else {
            imtheparent(pid_from_fork, run_in_background);
            /* Parent will continue around the loop. */
            command++;
         }
      } /* end if */
   } /* end while loop */

   return EXIT_SUCCESS;
} /* end mp2_main() */
